home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1997 August / Walnut Creek CDROM.7z / VOL_400 / 466_01 / SRC / FMTIF.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1996-12-20  |  15.4 KB  |  656 lines

  1. #include <afx.h>
  2. #include <afxtempl.h>
  3. #include "parse.h"
  4. #include "input.h"
  5. #include "fmt.h"
  6. #include "topiclog.h"
  7. #include "fmtif.h"
  8. #include "errmsg.h"
  9.  
  10. /////////////////////////////////////////////////////////////////////
  11.  
  12. int ParseIfXRef(const char *&sz, PIFCONDITION tail, int nFields);
  13. int ParseIfExpression(const char *&sz, PIFCONDITION tail, int nFields); 
  14.  
  15. /******************************************************/
  16. // Name Helper functions
  17. /******************************************************/
  18.  
  19.  
  20. void InitName(
  21.     PNAMETOKEN pNameTok,
  22.     int nTokens)
  23. {
  24.     for(int i = 0; i < nTokens; i++)
  25.     {
  26.         pNameTok[i].nField = 0;
  27.         pNameTok[i].nType = nameNone;
  28.         pNameTok[i].sTag.Empty();
  29.     }
  30. }    
  31.  
  32.  
  33. //@func Parses a single token within a topic name, either $1 for local field
  34. // or $tagname.1 for topic reference.
  35. //
  36. //@comm Caller is responsible for freeing memory allocated in <p pNameTok>.
  37. //
  38. //@rdesc Returns zero if successful or an error code.
  39.  
  40. int ParseNameToken(
  41.     const char *&sz,        //@parm Name token text.
  42.                             //  $ already parsed, starts with name reference
  43.     PNAMETOKEN pNameTok,    //@parm <t NAMETOKEN> structure to fill out.
  44.     int nFields)            //@parm Number of fields in tag.
  45. {
  46.     int nRet;
  47.     int i;
  48.     const char *szEnd;
  49.  
  50.     if(isdigit(*sz))
  51.     {
  52.         pNameTok->nType = nameLocal;
  53.         sz = MakeNumber(sz, pNameTok->nField);
  54.  
  55.         if(pNameTok->nField < 1 || pNameTok->nField > MAXNUMFIELDS)
  56.             return fmterrBadFieldReference;
  57.         
  58.         if(pNameTok->nField > nFields)
  59.             return fmterrFieldReferenceRange;
  60.  
  61.         pNameTok->nField--;
  62.     }
  63.     else if(*sz == chOpenAngle)
  64.     {
  65.         pNameTok->nType = nameLocalStripTemplate;
  66.         sz = MakeNumber(++sz, pNameTok->nField);
  67.  
  68.         if(pNameTok->nField < 1 || pNameTok->nField > MAXNUMFIELDS)
  69.             return fmterrBadFieldReference;
  70.         
  71.         if(pNameTok->nField > nFields)
  72.             return fmterrFieldReferenceRange;
  73.  
  74.         pNameTok->nField--;
  75.     }
  76.     else if(isalpha(*sz))
  77.     {
  78.         pNameTok->nType = nameTopic;
  79.  
  80.         // Get end of topic name 
  81.         
  82.         for(szEnd = sz, i = 0; istagchar(*szEnd) && i < MAXTAGSIZE; 
  83.                 szEnd++, i++);
  84.  
  85.         if(i == MAXTAGSIZE)
  86.             return fmterrNameBadSpec;
  87.             
  88.         // Make the string
  89.  
  90.         CString sTag(sz, i);
  91.  
  92.         pNameTok->sTag = sTag;
  93.  
  94.         sz = szEnd;
  95.                             
  96.         if(*sz++ != chPeriod)
  97.         {
  98.             nRet =  fmterrExpectedPeriod;
  99.             goto Err;
  100.         }
  101.                             
  102.         if(!isdigit(*sz))
  103.         {
  104.             nRet =  fmterrExpectedFieldNum;
  105.             goto Err;
  106.         }
  107.  
  108.         sz = MakeNumber(sz, pNameTok->nField);
  109.  
  110.         if(pNameTok->nField < 1 || pNameTok->nField > MAXNUMFIELDS)
  111.             return fmterrBadFieldReference;
  112.  
  113.         pNameTok->nField--;
  114.     }
  115.     else
  116.     {
  117.         nRet = fmterrNameBadSpec;
  118.         goto Err;
  119.     }
  120.  
  121.     return 0;
  122.  
  123. Err:
  124.  
  125.     return nRet;
  126. }
  127.  
  128.  
  129.  
  130.  
  131.  
  132. //@func Parses a topic name mask (such as $1::$2) and fills out an
  133. // array of <t NAMETOKEN> structures with the resulting topic name
  134. // tokens.
  135. //
  136. //@rdesc Returns zero if successful or an error code if failure.
  137. //
  138. //@comm If a parsing error occurs, the caller is responsible for 
  139. // freeing any memory created in the <p pNameTok> array.
  140.  
  141. int ParseTopicNameMask(
  142.     const char *&sz,         //@parm Source string with name spec.
  143.     PNAMETOKEN pNameTok,    //@parm <t NAMETOKEN> array to fill.
  144.     int &nTokens,             //@parm Token count to fill.
  145.     int nFields)
  146. {
  147.     int nRet;
  148.     const char *szEnd;
  149.     
  150.     static char szNameStopChars[] = " \t\r\n);,=";
  151.     static char szTokenStopChars[] = "$)";
  152.  
  153.     for(nTokens = 0; 
  154.         *sz && !strchr(szNameStopChars, *sz) && nTokens < MAXTOPICNAMETOKENS; 
  155.         nTokens++)
  156.     {
  157.         if(*sz == chDollar)
  158.         {
  159.             sz++;
  160.             
  161.             // sz is updated to end of token
  162.             nRet = ParseNameToken(sz, &pNameTok[nTokens], nFields);
  163.             if(nRet)
  164.                 return nRet;
  165.         }
  166.         else
  167.         {
  168.             // Get the end of the token.
  169.  
  170.             szEnd = SeekEnd(sz, szTokenStopChars, MAXTAGSIZE);
  171.             if(szEnd == NULL)
  172.                 return fmterrNameBadSpec;
  173.  
  174.             // Make a string.
  175.  
  176.             pNameTok[nTokens].sTag = CString(sz, szEnd-sz);
  177.             pNameTok[nTokens].nType = nameLiteral;
  178.  
  179.             sz = szEnd;
  180.         }
  181.     }
  182.     
  183.     if(*sz && !strchr(szNameStopChars, *sz))
  184.         return fmterrNameCount;
  185.  
  186.     return 0;
  187. }
  188.  
  189.  
  190.  
  191.  
  192. //@func This function creates a topic name given a format mask and an
  193. // array of fields. 
  194.  
  195.  
  196. void PrintName(
  197.     char *szNameBuf,        //@parm Buffer at least MAXTOPICNAMELEN+1 long
  198.     PNAMETOKEN pNameTok,    //@parm Name format specifiers: $1::$2 etc.
  199.     int nTokens,            //@parm Number of name format specifiers.
  200.     CTag *ptagTag,           //@parm TAG struct for tag
  201.     CTag *ptagTopic)            //@parm TAG struct for topi
  202. {
  203.     const char *szToken;
  204.     int i, j;
  205.     int cbToken;
  206.     int nNameLen;
  207.  
  208.     for(i = 0, nNameLen = 0; i < nTokens; i++)
  209.     {
  210.         switch(pNameTok[i].nType)
  211.         {
  212.         case nameLocalStripTemplate:
  213.         case nameLocal:
  214.  
  215.             szToken = ptagTag->aszFieldText[pNameTok[i].nField];
  216.  
  217.             // Empty token - copy nothing
  218.             if(szToken == NULL || *szToken == '\0')
  219.             {
  220.                 cbToken = 0;
  221.             }
  222.             
  223.             // Tag token
  224.             else if(pNameTok[i].nType == nameLocal)
  225.             {
  226.                 cbToken = strlen(szToken);
  227.             }
  228.             else
  229.             {
  230.                 // Find the angle bracket.
  231.  
  232.                 for(j = 0; szToken[j] && szToken[j] != chOpenAngle; j++);
  233.             
  234.                 if(szToken[j] == chOpenAngle && (j > 0 && szToken[j-1] == '\\'))
  235.                     j--;
  236.  
  237.                 cbToken = j;
  238.             }
  239.             
  240.             break;
  241.             
  242.         case nameTopic:
  243.  
  244.             szToken = ptagTopic->aszFieldText[pNameTok[i].nField];
  245.  
  246.  
  247.             if(szToken == NULL || *szToken == '\0' || 
  248.                _stricmp(pNameTok[i].sTag, ptagTopic->sTag) != 0)
  249.             {
  250.                 cbToken = 0;
  251.             }
  252.             else
  253.             {
  254.                 cbToken = strlen(szToken);
  255.             }
  256.             break;
  257.  
  258.         case nameLiteral:
  259.  
  260.             szToken = pNameTok[i].sTag;
  261.  
  262.             cbToken = strlen(szToken);
  263.             
  264.             break;
  265.             
  266.         default:
  267.             cbToken = 0;
  268.  
  269.             ASSERT(0);
  270.         }
  271.  
  272.  
  273.         // if necessary shorten token so it fits in field.
  274.  
  275.         if(nNameLen + cbToken > MAXTOPICNAMELEN)
  276.             cbToken = MAXTOPICNAMELEN-nNameLen;
  277.  
  278.         
  279.         // copy token over to recipient field, dealing with
  280.         // escaped characters.
  281.  
  282.         char *szNameBufCur;     // current recipient position
  283.         int  cbField;           // real count of chars (minus escapes)
  284.         int  k;                    // index into token
  285.         extern char szAutoduckEscape[];       // def in OUTPUT.CPP
  286.  
  287.         szNameBufCur = &szNameBuf[nNameLen];
  288.         for(cbField = 0, k = 0; k < cbToken; k++)
  289.         {
  290.             if(chBackslash == szToken[k] &&
  291.                strchr(szAutoduckEscape, szToken[k+1]))
  292.             {
  293.                 // skip backslash and output escaped
  294.                 // character
  295.  
  296.                 k++;
  297.             }
  298.  
  299.             if(szToken[k])
  300.                 szNameBufCur[cbField++] = szToken[k];
  301.         }
  302.  
  303.         nNameLen += cbField;
  304.     }
  305.  
  306.     szNameBuf[nNameLen] = '\0';
  307. }
  308.  
  309.  
  310.  
  311.  
  312.  
  313.  
  314. /////////////////////////////////////////////////////////////////////
  315.  
  316. /******************************************************/
  317. // If conditions - will eventually switch these over
  318. // to use the expression classes.
  319. /******************************************************/
  320.  
  321.  
  322. void InitIf(
  323.     PIFCONDITION pif)
  324. {
  325.     InitName(pif->apNameTok[0], MAXTOPICNAMETOKENS);
  326.     InitName(pif->apNameTok[1], MAXTOPICNAMETOKENS);
  327.  
  328.     pif->anTokens[0] = 0;
  329.     pif->anTokens[1] = 0;
  330.  
  331.     pif->nType = ifNone;
  332.     pif->nParam1 = 0;
  333.  
  334.     pif->next = NULL;
  335. }
  336.  
  337. //@func This function parses a type-formatting Xref expression, in the
  338. // form exists($1.$2). The calling function has already found the 
  339. // "exists" keyword.
  340.  
  341. int ParseIfXRef(
  342.     const char *&sz, 
  343.     PIFCONDITION tail,
  344.     int nFields)
  345. {
  346.     int nRet;
  347.  
  348.     tail->nType = ifTopicExists;
  349.  
  350.     // Skip past "exists" keyword and look for opening paren.
  351.  
  352.     sz = SeekChar(sz+6, chOpenParen);
  353.     if(NULL == sz)
  354.         return fmterrExpectedOpenParen;
  355.     
  356.     // Get topic name mask and construct token list.
  357.     
  358.     sz = EatWhite(++sz);
  359.     
  360.     nRet = ParseTopicNameMask(sz, tail->apNameTok[0], tail->anTokens[0], nFields);
  361.     if(nRet)
  362.         return nRet;
  363.         
  364.     // Find closing paren.
  365.  
  366.     sz = EatWhite(sz);    
  367.  
  368.     if(*sz != chCloseParen)
  369.         return fmterrExpectedCloseParen;
  370.  
  371.     // Advance past closing paren.
  372.     ++sz;
  373.  
  374.     return 0;
  375. }
  376.  
  377.  
  378. //@func This function parses a type-formatting .IF expression, in the
  379. // form $1==$func.1
  380.  
  381. int ParseIfExpression(
  382.     const char *&sz,        // @parm Pointer to format spec buffer
  383.     PIFCONDITION tail,      // @parm End of type if structure list
  384.     int nFields)
  385. {
  386.     int  nRet = 0;
  387.     int  i;
  388.  
  389.     tail->nType = ifExpression;
  390.     
  391.     // Get the left expression, equal sign, and right expression.
  392.  
  393.     for(i = 0; i < 2; i++)
  394.     {
  395.         // Get the operand - can be any combination of field references
  396.         // and regular text.
  397.         
  398.         nRet = ParseTopicNameMask(sz, tail->apNameTok[i], tail->anTokens[i], nFields);
  399.         if(nRet)
  400.             return nRet;
  401.  
  402.         // Only do the next stuff if we're on the left side 
  403.         // of the equal.
  404.  
  405.         if(i == 1)
  406.             break;
  407.                         
  408.         // Find the equal sign
  409.             
  410.         if((sz = SeekChar(sz, '=')) == NULL)
  411.             return fmterrExpectedEqual;
  412.  
  413.         // Find the start of the next operand.
  414.                             
  415.         sz = EatWhite(++sz);
  416.     }
  417.     
  418.     return 0;
  419. }
  420.  
  421.  
  422. /*
  423. @func This function parses the .IF tag text of a paragraph, text, or
  424. diagram tag. It constructs a linked list of IFCONDITION structures
  425. from the format-file text.
  426.  
  427. @rdesc Returns 0 if successful or an error code if unsuccessful.
  428. */
  429.  
  430. int GetIfCondition(
  431.     const char *sz,            // @parm Start of IF text
  432.     PIFCONDITION &pIf,      // @parm Where to drop head of linked list
  433.     int nFields)            // @parm How many fields in parent tag
  434. {
  435. TRY
  436. {
  437.     PIFCONDITION tail;
  438.     int nRet;
  439.     static char szNameTerm[] = " \t)";
  440.         
  441.     // Find the end of the tail, if any already exist
  442.  
  443.     for(tail = pIf; tail && tail->next; tail = tail->next);
  444.  
  445.     // Cycle through the IF statements, constructing a new IF
  446.     // structure for each.
  447.     
  448.     while(TRUE)
  449.     {
  450.         if(tail == NULL)
  451.         {
  452.             pIf = tail = new IFCONDITION;
  453.         }
  454.         else
  455.         {
  456.             tail->next = new IFCONDITION;
  457.             tail = tail->next;
  458.         }
  459.  
  460.         InitIf(tail);
  461.  
  462.         sz = EatWhite(sz);
  463.         if(*sz == chDollar)
  464.         {
  465.             nRet = ParseIfExpression(sz, tail, nFields);
  466.             if(nRet)
  467.                 return nRet;
  468.         }
  469.         else if(_strnicmp(sz, "exists", 6) == 0)
  470.         {
  471.             nRet = ParseIfXRef(sz, tail, nFields);
  472.             if(nRet)
  473.                 return nRet;
  474.         }
  475.         else if(_strnicmp(sz, "tagexists", 9) == 0)
  476.         {
  477.             tail->nType = ifTagExists;
  478.  
  479.             if((sz = SeekChar(sz+9, chOpenParen)) == NULL)
  480.                 return fmterrExpectedOpenParen;
  481.  
  482.             sz = EatWhite(++sz);
  483.                 
  484.             const char *szEnd = SeekEnd(sz, szNameTerm, MAXTAGSIZE);
  485.             if(NULL == szEnd)
  486.                 return fmterrBadTagName;
  487.  
  488.             tail->sParam1 = CString(sz, szEnd-sz);
  489.  
  490.             sz = szEnd;
  491.  
  492.             if((sz = SeekChar(sz, chCloseParen)) == NULL)
  493.                 return fmterrExpectedCloseParen;
  494.  
  495.             sz++;
  496.         }
  497.         else if(_strnicmp(sz, "fieldempty", 10) == 0)
  498.         {
  499.             tail->nType = ifFieldEmpty;
  500.  
  501.             if((sz = SeekChar(sz+10, chOpenParen)) == NULL)
  502.                 return fmterrExpectedOpenParen;
  503.  
  504.             if((sz = SeekNumber(++sz)) == NULL)
  505.                 return fmterrBadFieldReference;
  506.  
  507.             sz = MakeNumber(sz, tail->nParam1);
  508.             if(tail->nParam1 < 1 || tail->nParam1 > nFields)
  509.                 return fmterrBadFieldReference;
  510.  
  511.             tail->nParam1--;
  512.             
  513.             if((sz = SeekChar(sz, chCloseParen)) == NULL)
  514.                 return fmterrExpectedCloseParen;
  515.  
  516.             sz++;
  517.         }
  518.         else
  519.         {
  520.             return fmterrBadIfExpression;
  521.         }
  522.             
  523.         // See if we're done.
  524.         
  525.         if(*EatWhite(sz) == '\0')
  526.             break;
  527.             
  528.         // If we're not done, we should now have a comma to separate
  529.         // the IF statements.
  530.                         
  531.         if((sz = SeekChar(sz, chComma)) == NULL)
  532.             return fmterrExpectedComma;
  533.                         
  534.         // Advance past the comma, and allocate a new IF structure.
  535.  
  536.         sz++;
  537.     }
  538.  
  539.     return 0;
  540. }
  541. CATCH(CMemoryException, e)
  542. {
  543.     return errMemory;
  544. }
  545. END_CATCH
  546. }
  547.  
  548.  
  549.  
  550. BOOL EvalIfCondition(
  551.     PIFCONDITION pIf,        //@parm Conditions to evaluate
  552.     CTopicLog *pLog,        //@parm Topic log
  553.     CTagList *plistTags,    //@parm Topic tag list
  554.     CTag *ptagTag)            //@parm Field info for paragraph or text tag.
  555. {
  556. TRY
  557. {
  558.     char szNameBuf[MAXTOPICNAMELEN+1];
  559.     char szNameBuf2[MAXTOPICNAMELEN+1];
  560.     int nRet;
  561.  
  562.     CTag *ptagTopic = plistTags->GetHead();
  563.     ASSERT(ptagTopic);
  564.     
  565.     // Cycle through all the IF statements. Give back a FALSE if any
  566.     // of them don't match.
  567.     
  568.     for(; pIf; pIf = pIf->next)
  569.     {
  570.         switch(pIf->nType)
  571.         {
  572.         case ifTopicExists:
  573.         {
  574.             CTopic  *pTopic;
  575.  
  576.             if(pLog == NULL)
  577.                 return FALSE;
  578.  
  579.             PrintName(szNameBuf, pIf->apNameTok[0], pIf->anTokens[0],
  580.                                 ptagTag, ptagTopic);
  581.  
  582.             nRet = pLog->Search(szNameBuf, pTopic);
  583.             if(nRet == FALSE)
  584.                 return FALSE;
  585.  
  586.             break;
  587.         }
  588.                 
  589.         case ifExpression: 
  590.         {
  591.             CString sLeft;
  592.             CString sRight;
  593.  
  594.             // Try to retrieve expression operands, which can consist of
  595.             // field references (from entry or topic tag) and text. 
  596.  
  597.             PrintName(szNameBuf, pIf->apNameTok[0], pIf->anTokens[0], 
  598.                                 ptagTag, ptagTopic);
  599.  
  600.             PrintName(szNameBuf2, pIf->apNameTok[1], pIf->anTokens[1],
  601.                                 ptagTag, ptagTopic);
  602.             // Perform case-insensitive compare.
  603.             
  604.             if(_stricmp(szNameBuf, szNameBuf2))
  605.                 return FALSE;
  606.  
  607.             break;
  608.         }
  609.             
  610.         case ifTagExists:
  611.         {
  612.             CTag *ptag;
  613.             POSITION pos = plistTags->GetHeadPosition();
  614.             BOOL bFound = FALSE;
  615.  
  616.             while(pos)
  617.             {
  618.                 ptag = plistTags->GetNext(pos);
  619.  
  620.                 if(_stricmp(pIf->sParam1, ptag->sTag) == 0)
  621.                 {
  622.                     bFound = TRUE;
  623.                     break;
  624.                 }
  625.             }
  626.  
  627.             if(FALSE == bFound)
  628.                 return FALSE;
  629.  
  630.             break;
  631.         }
  632.                 
  633.         case ifFieldEmpty:
  634.             if(ptagTag->aszFieldText[pIf->nParam1]    != NULL && 
  635.                ptagTag->aszFieldText[pIf->nParam1][0] != '\0')
  636.             {
  637.                 return FALSE;
  638.             }
  639.             break;
  640.  
  641.         default:
  642.             ASSERT(0);
  643.  
  644.             return FALSE;
  645.         }
  646.     }
  647.     return TRUE;
  648. }
  649. CATCH(CMemoryException, e)
  650. {
  651.     return errMemory;
  652. }
  653. END_CATCH
  654. }
  655.  
  656.